package phases

import (
	"gopkg.openfuyao.cn/bkecommon"
	confv1beta1 "gopkg.openfuyao.cn/bkecommon/cluster/api/v1beta1"
	bkenode "gopkg.openfuyao.cn/bkecommon/cluster/node"
	ctrl "sigs.k8s.io/controller-runtime"

	bkev1beta1 "gopkg.openfuyao.cn/cluster-api-provider-bke/api/v1beta1"
	"gopkg.openfuyao.cn/cluster-api-provider-bke/pkg/command"
	"gopkg.openfuyao.cn/cluster-api-provider-bke/pkg/mergecluster"
	"gopkg.openfuyao.cn/cluster-api-provider-bke/pkg/phaseframe"
	"gopkg.openfuyao.cn/cluster-api-provider-bke/pkg/phaseframe/phaseutil"
	"gopkg.openfuyao.cn/cluster-api-provider-bke/utils"
	"gopkg.openfuyao.cn/cluster-api-provider-bke/utils/annotation"
	"gopkg.openfuyao.cn/cluster-api-provider-bke/utils/condition"
	"gopkg.openfuyao.cn/cluster-api-provider-bke/utils/constant"
)

const (
	EnsureAgentSwitchName confv1beta1.BKEClusterPhase = "EnsureAgentSwitch"
)

type EnsureAgentSwitch struct {
	phaseframe.BasePhase
}

func NewEnsureAgentSwitch(ctx *phaseframe.PhaseContext) phaseframe.Phase {
	base := phaseframe.NewBasePhase(ctx, EnsureAgentSwitchName)
	return &EnsureAgentSwitch{base}
}

func (e *EnsureAgentSwitch) Execute() (_ ctrl.Result, err error) {
	if err = e.reconcileAgentSwitch(); err != nil {
		return ctrl.Result{}, err
	}
	return ctrl.Result{}, nil
}

func (e *EnsureAgentSwitch) NeedExecute(old *bkev1beta1.BKECluster, new *bkev1beta1.BKECluster) (needExecute bool) {
	if !e.BasePhase.DefaultNeedExecute(old, new) {
		return false
	}

	if listener, ok := annotation.HasAnnotation(e.Ctx.BKECluster, bkecommon.BKEAgentListenerAnnotationKey); ok && listener == bkecommon.BKEAgentListenerCurrent {
		return false
	}

	if condition.HasConditionStatus(bkev1beta1.SwitchBKEAgentCondition, e.Ctx.BKECluster, confv1beta1.ConditionTrue) {
		return false
	}

	// 部署没有结束不要切换
	if !phaseutil.ClusterEndDeployed(e.Ctx.Context, e.Ctx.Client, e.Ctx.Cluster, e.Ctx.BKECluster) {
		return false
	}

	e.SetStatus(bkev1beta1.PhaseWaiting)
	return true
}

func (e *EnsureAgentSwitch) reconcileAgentSwitch() error {
	ctx, c, bkeCluster, scheme, log := e.Ctx.Untie()

	listener, ok := annotation.HasAnnotation(bkeCluster, bkecommon.BKEAgentListenerAnnotationKey)
	switch {
	case !ok:
		annotation.SetAnnotation(bkeCluster, bkecommon.BKEAgentListenerAnnotationKey, bkecommon.BKEAgentListenerCurrent)
		log.Info(constant.SwitchClusterSuccessReason, "skip switch BKEAgent listener, BKEAgent is listening current cluster")
		return mergecluster.SyncStatusUntilComplete(c, bkeCluster)
	case listener == bkecommon.BKEAgentListenerCurrent || !ok:
		log.Info(constant.SwitchClusterSuccessReason, "skip switch BKEAgent listener, BKEAgent is listening current cluster")
		return nil
	case listener == bkecommon.BKEAgentListenerBkecluster:
		log.Info(constant.SwitchClusterSuccessReason, "switch BKEAgent to listen BKECluster")
		switchCommand := command.Switch{
			BaseCommand: command.BaseCommand{
				Ctx:         ctx,
				Client:      c,
				NameSpace:   bkeCluster.Namespace,
				Scheme:      scheme,
				OwnerObj:    bkeCluster,
				ClusterName: bkeCluster.Name,
				Unique:      true,
			},
			Nodes:       bkenode.Nodes(bkeCluster.Spec.ClusterConfig.Nodes),
			ClusterName: bkeCluster.Name,
		}
		if err := switchCommand.New(); err != nil {
			log.Finish(constant.SwitchClusterFailedReason, "BKEAgent switch BKECluster %q failed: %s", utils.ClientObjNS(bkeCluster), err.Error())
			return nil
		}
		log.Info(constant.SwitchClusterSuccessReason, "BKEAgent switch BKECluster %q success", utils.ClientObjNS(bkeCluster))
		condition.ConditionMark(bkeCluster, bkev1beta1.SwitchBKEAgentCondition, confv1beta1.ConditionTrue, constant.SwitchClusterSuccessReason, "switch BKECluster %q success")

		return nil
	default:
		return nil
	}
}
